use crate::dto;
use crate::dto::core::endpoint::EndpointType;
use crate::service::Error;
use crate::service::endpoint::{BaseEndpointService, EndpointService};
use crate::service::endpoint_peer_manager::EndpointPeerManagerService;
use crate::service::endpoint_srv6_config::EndpointSrv6ConfigService;
use crate::service::endpoint_wireguard_config::EndpointWireguardConfigService;
use std::sync::Arc;
use wanmesh_common::error::ResponseError;

#[async_trait::async_trait]
pub trait EndpointManagerService: BaseEndpointService {
    async fn add(
        &self,
        params: &dto::endpoint::AddParams,
    ) -> Result<dto::core::endpoint::AddResponseData, Error>;

    async fn list_info(
        &self,
        filter: &dto::core::endpoint::Filter,
        offset: u64,
        limit: u64,
    ) -> Result<Vec<dto::endpoint::EndpointInfo>, Error>;

    async fn get_info(&self, endpoint_id: &str) -> Result<dto::endpoint::EndpointInfo, Error>;

    async fn try_get_info(
        &self,
        endpoint_id: &str,
    ) -> Result<Option<dto::endpoint::EndpointInfo>, Error>;
}

pub struct DefaultEndpointManagerService {
    endpoint_service: Arc<dyn EndpointService>,
    endpoint_srv6_config_service: Arc<dyn EndpointSrv6ConfigService>,
    endpoint_wireguard_config_service: Arc<dyn EndpointWireguardConfigService>,
    endpoint_peer_manager_service: Arc<dyn EndpointPeerManagerService>,
}

impl DefaultEndpointManagerService {
    pub fn new(
        endpoint_service: Arc<dyn EndpointService>,
        endpoint_srv6_config_service: Arc<dyn EndpointSrv6ConfigService>,
        endpoint_wireguard_config_service: Arc<dyn EndpointWireguardConfigService>,
        endpoint_peer_manager_service: Arc<dyn EndpointPeerManagerService>,
    ) -> Self {
        Self {
            endpoint_service,
            endpoint_srv6_config_service,
            endpoint_wireguard_config_service,
            endpoint_peer_manager_service,
        }
    }

    async fn get_info_by_endpoint_item(
        &self,
        endpoint: dto::core::endpoint::EndpointItem,
    ) -> Result<dto::endpoint::EndpointInfo, Error> {
        let endpoint_srv6_config = self
            .endpoint_srv6_config_service
            .get(endpoint.endpoint_id.as_str())
            .await?;

        let endpoint_wireguard_config = self
            .endpoint_wireguard_config_service
            .try_get(endpoint.endpoint_id.as_str())
            .await?;

        let endpoint_peer_configs = self
            .endpoint_peer_manager_service
            .all_info(&dto::core::endpoint_peer::Filter {
                endpoint_id: Some(endpoint.endpoint_id.clone()),
                ..Default::default()
            })
            .await?;

        Ok(dto::endpoint::EndpointInfo {
            inner: endpoint,
            srv6: endpoint_srv6_config,
            wireguard: endpoint_wireguard_config,
            peers: endpoint_peer_configs,
        })
    }
}

#[async_trait::async_trait]
impl BaseEndpointService for DefaultEndpointManagerService {
    async fn list(
        &self,
        filter: &dto::core::endpoint::Filter,
        offset: u64,
        limit: u64,
    ) -> Result<Vec<dto::core::endpoint::EndpointItem>, Error> {
        self.endpoint_service.list(filter, offset, limit).await
    }

    async fn get(&self, endpoint_id: &str) -> Result<dto::core::endpoint::EndpointItem, Error> {
        self.endpoint_service.get(endpoint_id).await
    }

    async fn try_get(
        &self,
        endpoint_id: &str,
    ) -> Result<Option<dto::core::endpoint::EndpointItem>, Error> {
        self.endpoint_service.try_get(endpoint_id).await
    }

    async fn update(
        &self,
        endpoint_id: &str,
        params: &dto::core::endpoint::UpdateParams,
    ) -> Result<(), Error> {
        self.endpoint_service.update(endpoint_id, params).await
    }

    async fn delete(&self, endpoint_id: &str) -> Result<(), Error> {
        let peers = self
            .endpoint_peer_manager_service
            .all(&dto::core::endpoint_peer::Filter {
                endpoint_id: Some(endpoint_id.to_string()),
                ..Default::default()
            })
            .await?;

        for peer in &peers {
            self.endpoint_peer_manager_service
                .delete(peer.inner.peer_endpoint_id.as_str())
                .await?;
        }

        self.endpoint_srv6_config_service
            .delete(endpoint_id)
            .await?;

        self.endpoint_wireguard_config_service
            .delete(endpoint_id)
            .await?;

        self.endpoint_service.delete(endpoint_id).await
    }

    async fn count(&self, filter: &dto::core::endpoint::Filter) -> Result<u64, Error> {
        self.endpoint_service.count(filter).await
    }

    async fn exists(&self, filter: &dto::core::endpoint::Filter) -> Result<bool, Error> {
        self.endpoint_service.exists(filter).await
    }
}

#[async_trait::async_trait]
impl EndpointManagerService for DefaultEndpointManagerService {
    async fn add(
        &self,
        params: &dto::endpoint::AddParams,
    ) -> Result<dto::core::endpoint::AddResponseData, Error> {
        match params.inner.endpoint_type {
            EndpointType::Srv6OverWireguard => {
                if self
                    .endpoint_service
                    .exists(&dto::core::endpoint::Filter {
                        node_id: Some(params.inner.node_id.clone()),
                        endpoint_id: None,
                        endpoint_type: Some(EndpointType::Srv6OverWireguard),
                        name: None,
                    })
                    .await?
                {
                    return Err(ResponseError::the_endpoint_type_only_have_one().into());
                }
            }
        }

        let data = self.endpoint_service.add(&params.inner).await?;

        self.endpoint_srv6_config_service
            .add(data.endpoint_id.as_str(), &params.srv6)
            .await?;

        match params.inner.endpoint_type {
            EndpointType::Srv6OverWireguard => {
                self.endpoint_wireguard_config_service
                    .add(
                        data.endpoint_id.as_str(),
                        &params.wireguard.clone().map_or(Default::default(), |v| v),
                    )
                    .await?;
            }
        }

        Ok(data)
    }

    async fn list_info(
        &self,
        filter: &dto::core::endpoint::Filter,
        offset: u64,
        limit: u64,
    ) -> Result<Vec<dto::endpoint::EndpointInfo>, Error> {
        let endpoints = self.list(filter, offset, limit).await?;
        let mut config_list = Vec::new();
        for endpoint in &endpoints {
            let config = self.get_info_by_endpoint_item(endpoint.clone()).await?;
            config_list.push(config);
        }
        Ok(config_list)
    }

    async fn get_info(&self, endpoint_id: &str) -> Result<dto::endpoint::EndpointInfo, Error> {
        let config = self
            .try_get_info(endpoint_id)
            .await?
            .ok_or(ResponseError::endpoint_not_found(endpoint_id))?;
        Ok(config)
    }

    async fn try_get_info(
        &self,
        endpoint_id: &str,
    ) -> Result<Option<dto::endpoint::EndpointInfo>, Error> {
        let endpoint = match self.try_get(endpoint_id).await? {
            None => return Ok(None),
            Some(v) => v,
        };

        let config = self.get_info_by_endpoint_item(endpoint).await?;
        Ok(Some(config))
    }
}
